package main

import (
	"bufio"
	"fmt"
	"os"
	"runtime"
	"strings"
)

//创建一个自定义的错误类型
func myError(text string) errorString {
	return errorString{text}
}

//错误信息的数据结构
type errorString struct {
	s string
}

//错误的方法：打印错误信息并返回错误信息
func (e *errorString) Error() string {
	fmt.Fprintf(os.Stdout, e.s)
	return e.s
}

//根据路径参数读取配置文件
//并且返回一个储存配置文件键值对的map和自定义的错误类型
func readFile(iniFile string, expectedSection string) (map[string]string, errorString) {
	config := map[string]string{} //储存配置文件信息
	var e errorString            //自定义的错误类型
	
	if expectedSection == "" {return config, e}

	file, err := os.Open(iniFile)
	
	if err != nil {
		e = myError("can not open file")
		return config, e
	}

	// 在函数结束时，关闭文件
	defer file.Close()
	
	//读取输入流
	reader := bufio.NewReader(file)
	// 当前读取的段的名字
	sectionName := ""
	//进行循环读取文件逐行进行文件读取
	for {
		// 读取文件的一行
        	linestr, err := reader.ReadString('\n')
        	if err != nil {
            		break
        	}
        	// 切掉行的左右两边的空白字符
	        linestr = strings.TrimSpace(linestr)
        	// 忽略空行
	        if linestr == "" {
        		continue
        	}
	        // 忽略注释
        	if linestr[0] == ';' ||  linestr[0] == '#'{
        		continue
	        }
        	// 行首和尾巴分别是方括号的，说明是段标记的起止符
        	if linestr[0] == '[' && linestr[len(linestr)-1] == ']' {
            		// 将段名取出
            		sectionName = linestr[1 : len(linestr)-1]
            		
            		fmt.Printf("%s:\n", sectionName)
            		// 这个段是希望读取的
        	} else if sectionName == expectedSection {
            		// 切开等号分割的键值对
            		pair := strings.Split(linestr, "=")
            		// 保证切开只有1个等号分割的简直情况
            		if len(pair) == 2 {
               		// 去掉键的多余空白字符
	       	        key := strings.TrimSpace(pair[0])
        		        value := strings.TrimSpace(pair[1])
               	     	config[key] = strings.TrimSpace(pair[1])
               	     	fmt.Printf("%s = %s\n", key, value)
            		}
        	}		
	}
	return config, e
}

//比较两个map是否相同
func mapCompare(map1 map[string]string, map2 map[string]string) bool {
	for key1, value1 := range map1 {
		value2, _ := map2[key1]
		if value1 != value2 {
			return false
		}
	}
	
	if len(map2) > len(map1) {
		return false
	}
	return true
}

//函数签名，定义一个参数为string的函数类型
type ListenFunc func(string, string)

//接口
type Listener interface {
	listen(inifile string, expectedSection string)
}


//函数签名ListenFunc实现接口方法
func (Listen ListenFunc) listen(inifile string, expectedSection string) {
	changeConfig, _ := readFile(inifile, expectedSection)
	print("Listening :\n")
	for {
		config, _ := readFile(inifile, expectedSection)
		if !mapCompare(changeConfig, config) {
			break
		}
	}
}

var symbol byte //注释符
//判断系统类型，根据系统类型判断注释符
func initSys() {
	sysType := runtime.GOOS
	if sysType == "linux" {
		symbol = '#'
	} else if sysType == "window" {
		symbol = ';'
	}
}

//读取文件，然后监听文件，如果文件在监听期间修改，则打印返回最新文件，并结束
func WatchWithOptions(iniFile string, expectedSection string ,Listen Listener) (map[string]string, errorString) {
	//判断系统类型，根据系统类型判断注释符
	initSys()

	config, err := readFile(iniFile, expectedSection)
	//存在错误，报错并结束
	if len(err.s) > 0 {
		fmt.Printf("error : %s", err.s)
		return config, err
	}
	//打印配置文件信息
	readFile(iniFile, expectedSection)
	//开启监听文件
	Listen.listen(iniFile, expectedSection)
	//文件被修改：
	print("The configuration file has changed :\n")
	//读取更新后的文件
	config, err = readFile(iniFile, expectedSection)
	if len(err.s) > 0 {
		fmt.Printf("error : %s", err.s)
	}
	return config, err
}

func main() {
	var listener Listener 
	listener = ListenFunc(func(string, string) {})
	WatchWithOptions("config.ini", "paths", listener)
}
